package com.webchat.pgc.service.mall;

import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.enums.mall.MallStatusEnum;
import com.webchat.common.service.RedisService;
import com.webchat.common.util.JsonUtil;
import com.webchat.common.util.StringUtil;
import com.webchat.common.util.TransactionSyncManagerUtil;
import com.webchat.domain.vo.request.mall.MallPropertyRequestVO;
import com.webchat.domain.vo.response.mall.MallPropertyResponseVO;
import com.webchat.pgc.repository.dao.mall.IMallPropertyDAO;
import com.webchat.pgc.repository.dao.mall.IMallPropertyValueDAO;
import com.webchat.pgc.repository.dao.mall.IMallShopPropertyDAO;
import com.webchat.pgc.repository.dao.mall.IMallShopPropertyValueDAO;
import com.webchat.pgc.repository.entity.mall.MallProperty;
import com.webchat.pgc.repository.entity.mall.MallPropertyValue;
import com.webchat.pgc.repository.entity.mall.MallShopProperty;
import com.webchat.pgc.repository.entity.mall.MallShopPropertyValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Slf4j
@Service
public class MallPropertyService {

    @Autowired
    private IMallPropertyDAO mallPropertyDAO;
    @Autowired
    private IMallPropertyValueDAO mallPropertyValueDAO;
    @Autowired
    private IMallShopPropertyDAO mallShopPropertyDAO;
    @Autowired
    private IMallShopPropertyValueDAO mallShopPropertyValueDAO;
    @Autowired
    private RedisService redisService;

    /**
     * 创建/更新商品销售属性名
     *
     * @return
     */
    @Transactional
    public Long savePropertyName(MallPropertyRequestVO request) {
        // 1. 持久化属性配置
        boolean isFirstCreate = request.getId() == null;
        MallProperty mallProperty = this.convertPropertyNameEntity(request);
        mallProperty = mallPropertyDAO.save(mallProperty);
        if (!mallProperty.getIsDefault() && isFirstCreate) {
            // 2. 自定义属性，需要创建或更新店铺自定义属性关系
            saveShopPropertyName(mallProperty.getId(), request.getShopId(), request.getOperator());
        }
        // 3.事物提交后处理（异步）
        doRefreshPropertyNameCache(mallProperty);
        return mallProperty.getId();
    }

    /**
     * 创建/更新商品销售属性值
     *
     * @return
     */
    @Transactional
    public Long savePropertyValue(MallPropertyRequestVO request) {

        // 1. 持久化属性配置
        boolean isFirstCreate = request.getId() == null;
        MallPropertyValue mallPropertyValue = this.convertPropertyValueEntity(request);
        mallPropertyValue = mallPropertyValueDAO.save(mallPropertyValue);
        if (!mallPropertyValue.getIsDefault() && isFirstCreate) {
            // 2. 自定义属性，需要创建或更新店铺自定义属性关系
            saveShopPropertyName(mallPropertyValue.getId(), request.getShopId(), request.getOperator());
        }
        // 3.事物提交后处理（异步）
        doRefreshPropertyValueCache(mallPropertyValue);
        return mallPropertyValue.getId();
    }

    public Boolean publishPropertyName(Long id, String operator) {

        boolean res = doUpdatePropertyNameStatus(id, MallStatusEnum.PUBLISHED, operator);
        log.info("销售属性发布 ===> 操作人：{} 属性ID:{}", operator, id);
        return res;
    }

    public Boolean publishPropertyValue(Long id, String operator) {

        boolean res = doUpdatePropertyValueStatus(id, MallStatusEnum.PUBLISHED, operator);
        log.info("销售属性值发布 ===> 操作人：{} 属性ID:{}", operator, id);
        return res;
    }

    public Boolean revokePropertyName(Long id, String operator) {

        boolean res = doUpdatePropertyNameStatus(id, MallStatusEnum.UNPUBLISHED, operator);
        log.info("销售属性撤回 ===> 操作人：{} 属性ID:{}", operator, id);
        return res;
    }

    public Boolean revokePropertyValue(Long id, String operator) {

        boolean res = doUpdatePropertyValueStatus(id, MallStatusEnum.UNPUBLISHED, operator);
        log.info("销售属性值撤回 ===> 操作人：{} 属性ID:{}", operator, id);
        return res;
    }

    public Boolean deletePropertyName(Long id, String operator) {

        boolean res = doUpdatePropertyNameStatus(id, MallStatusEnum.DELETED, operator);
        log.info("销售属性删除 ===> 操作人：{} 属性ID:{}", operator, id);
        return res;
    }

    public Boolean deletePropertyValue(Long id, String operator) {

        boolean res = doUpdatePropertyValueStatus(id, MallStatusEnum.DELETED, operator);
        log.info("销售属性值删除 ===> 操作人：{} 属性ID:{}", operator, id);
        return res;
    }

    /**
     * 更新销售属性名状态
     *
     * @param id
     * @param status
     * @param operator
     * @return
     */
    private boolean doUpdatePropertyNameStatus(Long id, MallStatusEnum status, String operator) {

        MallProperty propertyName = this.getPropertyName(id);
        Assert.notNull(propertyName, "销售属性名不存在");
        propertyName.setStatus(status.getStatus());
        propertyName.setUpdateBy(operator);
        propertyName.setUpdateDate(new Date());
        mallPropertyDAO.save(propertyName);
        // 刷新缓存
        this.doRefreshPropertyNameCache(propertyName);
        return true;
    }

    /**
     * 更新销售属性值状态
     *
     * @param id
     * @param status
     * @param operator
     * @return
     */
    private boolean doUpdatePropertyValueStatus(Long id, MallStatusEnum status, String operator) {

        MallPropertyValue propertyValue = this.getPropertyValue(id);
        Assert.notNull(propertyValue, "销售属性值不存在");
        propertyValue.setStatus(status.getStatus());
        propertyValue.setUpdateBy(operator);
        propertyValue.setUpdateDate(new Date());
        mallPropertyValueDAO.save(propertyValue);
        return true;
    }

    /**
     * 保存店铺自定义销售属性
     *
     * @return
     */
    private Long saveShopPropertyName(Long propertyNameId, Long shopId, String operator) {
        MallShopProperty shopProperty = new MallShopProperty();
        shopProperty.setShopId(shopId);
        shopProperty.setPropertyNameId(propertyNameId);
        shopProperty.setCreateBy(operator);
        shopProperty.setCreateDate(new Date());
        return mallShopPropertyDAO.save(shopProperty).getId();
    }

    /**
     * 保存店铺自定义销售属性值
     *
     * @return
     */
    private Long saveShopPropertyValue(Long propertyValueId, Long shopId, String operator) {
        MallShopPropertyValue shopPropertyValue = new MallShopPropertyValue();
        shopPropertyValue.setShopId(shopId);
        shopPropertyValue.setPropertyValueId(propertyValueId);
        shopPropertyValue.setCreateBy(operator);
        shopPropertyValue.setCreateDate(new Date());
        return mallShopPropertyValueDAO.save(shopPropertyValue).getId();
    }

    /**
     * 刷新销售属性名称缓存
     *
     * @return
     */
    public MallPropertyResponseVO doRefreshPropertyNameCache(Long id) {
        MallProperty mallProperty = getPropertyName(id);
        return this.doRefreshPropertyNameCache(mallProperty);
    }

    public MallPropertyResponseVO doRefreshPropertyNameCache(MallProperty mallProperty) {
        if (mallProperty == null) {
            return null;
        }
        String key = RedisKeyEnum.MALL_PROPERTY_NAME_CACHE.getKey();
        MallPropertyResponseVO vo = convertPropertyVO(mallProperty);
        redisService.hset(key,
                          String.valueOf(mallProperty.getId()),
                          JsonUtil.toJsonString(vo),
                          RedisKeyEnum.MALL_PROPERTY_NAME_CACHE.getExpireTime());
        return vo;
    }

    /**
     * 刷新商品销售属性值缓存
     *
     * @param id
     * @return
     */
    public MallPropertyResponseVO doRefreshPropertyValueCache(Long id) {
        MallPropertyValue propertyValue = getPropertyValue(id);
        return this.doRefreshPropertyValueCache(propertyValue);
    }

    public MallPropertyResponseVO doRefreshPropertyValueCache(MallPropertyValue propertyValue) {

        if (propertyValue == null) {
            return null;
        }
        String key = RedisKeyEnum.MALL_PROPERTY_VALUE_CACHE.getKey();
        MallPropertyResponseVO vo = convertPropertyVO(propertyValue);
        redisService.hset(key,
                String.valueOf(propertyValue.getId()),
                JsonUtil.toJsonString(vo),
                RedisKeyEnum.MALL_PROPERTY_VALUE_CACHE.getExpireTime());
        return vo;
    }

    /**
     * 查询属性名
     *
     * @param id
     * @return
     */
    public MallPropertyResponseVO getPropertyNameFromCache(Long id) {
        String key = RedisKeyEnum.MALL_PROPERTY_NAME_CACHE.getKey();
        String cache = redisService.hget(key, String.valueOf(id));
        if (StringUtils.isBlank(cache)) {
            return this.doRefreshPropertyNameCache(id);
        }
        return JsonUtil.fromJson(cache, MallPropertyResponseVO.class);
    }

    /**
     * 查询属性值
     *
     * @param id
     * @return
     */
    public MallPropertyResponseVO getPropertyValueFromCache(Long id) {
        String key = RedisKeyEnum.MALL_PROPERTY_VALUE_CACHE.getKey();
        String cache = redisService.hget(key, String.valueOf(id));
        if (StringUtils.isBlank(cache)) {
            return this.doRefreshPropertyNameCache(id);
        }
        return JsonUtil.fromJson(cache, MallPropertyResponseVO.class);
    }

    /**
     * 走缓存批量获取属性名称
     *
     * @param propertyNameIds
     * @return
     */
    public Map<Long, MallPropertyResponseVO> getPropertyNameMap(List<Long> propertyNameIds) {
        if (CollectionUtils.isEmpty(propertyNameIds)) {
            return Collections.emptyMap();
        }
        Map<Long, MallPropertyResponseVO> propertyMap = new HashMap<>();
        String key = RedisKeyEnum.MALL_PROPERTY_NAME_CACHE.getKey();
        List<String> fields = propertyNameIds.stream().map(String::valueOf).toList();
        List<String> caches = redisService.hmget(key, fields);
        for (int i = 0; i < propertyNameIds.size(); i++) {
            Long propertyNameId = propertyNameIds.get(i);
            String cache = caches.get(i);
            MallPropertyResponseVO property;
            if (StringUtils.isNotBlank(cache)) {
                property = JsonUtil.fromJson(cache, MallPropertyResponseVO.class);
            } else {
                property = doRefreshPropertyNameCache(propertyNameId);
            }
            propertyMap.put(propertyNameId, property);
        }
        return propertyMap;
    }

    /**
     * 走缓存批量获取属性值
     *
     * @param propertyValueIds
     * @return
     */
    public Map<Long, MallPropertyResponseVO> getPropertyValueMap(List<Long> propertyValueIds) {

        if (CollectionUtils.isEmpty(propertyValueIds)) {
            return Collections.emptyMap();
        }
        Map<Long, MallPropertyResponseVO> propertyMap = new HashMap<>();
        String key = RedisKeyEnum.MALL_PROPERTY_VALUE_CACHE.getKey();
        List<String> fields = propertyValueIds.stream().map(String::valueOf).toList();
        List<String> caches = redisService.hmget(key, fields);
        for (int i = 0; i < propertyValueIds.size(); i++) {
            Long propertyIdId = propertyValueIds.get(i);
            String cache = caches.get(i);
            MallPropertyResponseVO property;
            if (StringUtils.isNotBlank(cache)) {
                property = JsonUtil.fromJson(cache, MallPropertyResponseVO.class);
            } else {
                property = doRefreshPropertyValueCache(propertyIdId);
            }
            propertyMap.put(propertyIdId, property);
        }
        return propertyMap;
    }

    public List<MallPropertyResponseVO> suggestPropertyName(String keyword, int size) {

        // TODO

        return null;
    }

    public List<MallPropertyResponseVO> suggestPropertyValue(String keyword, int size) {

        // TODO

        return null;
    }

    /**
     * 翻页获取销售属性列表
     *
     * @param keyword
     * @param isDefault
     * @param pageNo
     * @param pageSize
     * @return
     */
    public List<MallPropertyResponseVO> pagePropertyName(String keyword, Boolean isDefault,
                                                         int pageNo, int pageSize) {

        // TODO

        return null;
    }

    /**
     * 翻页获取销售属性值列表
     *
     * @param keyword
     * @param isDefault
     * @param pageNo
     * @param pageSize
     * @return
     */
    public List<MallPropertyResponseVO> pagePropertyValue(String keyword, Boolean isDefault,
                                                          int pageNo, int pageSize) {

        // TODO

        return null;
    }


    private MallProperty getPropertyName(Long id) {
        return mallPropertyDAO.findById(id).orElse(null);
    }

    private MallPropertyValue getPropertyValue(Long id) {
        return mallPropertyValueDAO.findById(id).orElse(null);
    }

    private MallProperty convertPropertyNameEntity(MallPropertyRequestVO request) {

        Long id = request.getId();
        String operator = request.getOperator();
        Date now = new Date();
        MallProperty property;
        if (id != null) {
            property = mallPropertyDAO.findById(id).orElse(null);
            Assert.notNull(property, "更新失败：属性不存在");
        } else {
            property = new MallProperty();
            property.setStatus(MallStatusEnum.NEW.getStatus());
            property.setCreateBy(operator);
            property.setCreateDate(now);
        }
        property.setName(request.getVal());
        property.setIsDefault(ObjectUtils.equals(request.getIsDefault(), true));
        property.setUpdateBy(operator);
        property.setUpdateDate(now);
        return property;
    }

    private MallPropertyValue convertPropertyValueEntity(MallPropertyRequestVO request) {

        Long id = request.getId();
        String operator = request.getOperator();
        Date now = new Date();
        MallPropertyValue property;
        if (id != null) {
            property = mallPropertyValueDAO.findById(id).orElse(null);
            Assert.notNull(property, "更新失败：属性不存在");
        } else {
            property = new MallPropertyValue();
            property.setStatus(MallStatusEnum.NEW.getStatus());
            property.setCreateBy(operator);
            property.setCreateDate(now);
        }
        property.setValue(request.getVal());
        property.setIsDefault(ObjectUtils.equals(request.getIsDefault(), true));
        property.setUpdateBy(operator);
        property.setUpdateDate(now);
        return property;
    }

    private MallPropertyResponseVO convertPropertyVO(MallProperty property) {
        MallPropertyResponseVO vo = new MallPropertyResponseVO();
        vo.setId(property.getId());
        vo.setVal(property.getName());
        vo.setStatus(property.getStatus());
        return vo;
    }

    private MallPropertyResponseVO convertPropertyVO(MallPropertyValue propertyValue) {
        MallPropertyResponseVO vo = new MallPropertyResponseVO();
        vo.setId(propertyValue.getId());
        vo.setVal(propertyValue.getValue());
        vo.setStatus(propertyValue.getStatus());
        return vo;
    }
}
